package com.newtribe.access;


import java.io.*;
import java.net.*;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import java.util.LinkedList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * <p>Description: The Server super class </p>
 * <p>Copyright: Copyright (c) 2004</p>
 * <p>Company: ShenYang newtribe.external Corp .</p>
 * @author fuwei ,fuw@newtribe.external.com
 * @version 1.0
 */

public abstract class Server implements Runnable {
  public Server() {
  }
  /**
   * get timeout times
   * @return
   */
  protected abstract int getTimeOut();
  /**
   * get max processors
   * @return
   */
  protected abstract int getProcessors();
  /**
   * get configed listen port
   * @return
   */
  protected abstract int getListenPort();
  /**
   * get concrete service name .
   * @return
   */
  protected abstract String getServiceName() ;

  /**
   *  create a new Processor class .
   */
  protected abstract Processor newProcessor();
  /**
   *
   * @throws java.lang.Exception
   */
  public void start()  {
    Thread t = new Thread(this);
    this.sequence = 0;
    t.start();

  }

  protected void threadDoStart() throws Exception {
    //we have start the server .
    if (serverSocket != null)
      return;
    try {

      serverSocket = new ServerSocket( getListenPort() ,0 ,InetAddress.getByName("0.0.0.0")
                                       );
      System.out.println(getServiceName() +"Server Start ....");
      System.out.println("Processor [" + this.getProcessors()  + "]");
      System.out.println("Listen port [" + getListenPort() + "]");
      stop = false;
      //initialize processors
      for (int i = 0; i < getProcessors(); i++) {
        Processor  processor = newProcessor();
        processor.start() ;
        free.add(processor) ;
        sequence ++ ;
      }

      mainLoop();

    }
    catch (Exception e) {
      e.printStackTrace() ;
      log.fatal("an error occured ,can't startup normally!");
      stop();
    }

  }

  public final void run() {
    try {
      threadDoStart();
    }
    catch (Throwable t) {
      t.printStackTrace();
    }
  }

  void mainLoop() {
    //Socket socket =null ;
    while (!stop) {
      Socket socket = null;
      
      try {
    	  
        socket = this.serverSocket.accept();
        socket.setSoTimeout(getTimeOut());
        socket.setKeepAlive(true);
        log.info(" accept request from" + socket.getInetAddress());
        Processor processor = findProcessor();
        if (processor == null) {
          log.error("cant' allocate a processor ! :" + socket.getInetAddress().getHostAddress());
          socket.close(); //stop request .
          continue;
        }

        try {

          processor.assign(socket);
          

        }
        catch (Exception alle) {
        	alle.printStackTrace();
          //recycle(processor);

        }

//     synchronized (processor) {
//       processor.notifyAll();
//     }
      }
      catch (SocketException nete) {
        nete.printStackTrace();
      }

      catch (IOException ioe) {
        ioe.printStackTrace();
      }

      catch (Exception e) {
        e.printStackTrace();
      }

    }
  }

  protected HashMap busy = new HashMap();
  protected LinkedList free = new LinkedList();
  /**
   *
   * @return process pool
   */
  public HashMap getBusyProcessor() {
    return this.busy;
  }

  /**
   * for manager trace information .
   * @return a LinkedList contais processor
   */
  public LinkedList getFreeProcessor() {
    return this.free;
  }

  /**
   * find available processors .
   * when all is busy processing request now ,null is returned .
   * @return s
   */
  private synchronized Processor findProcessor() {
    if (free.size() != 0) {
      Processor processor = (Processor) free.removeFirst();
      busy.put(processor, processor);
      return processor;
    }
    return null;
  }

  /**
   *
   * @param processor Processor in Pool
   */
  public synchronized void recycle(Processor processor) {
    free.addLast(processor);
    busy.remove(processor);
  }
 //sub processor sequence .
  protected int sequence = 0;


  /**
   *
   * @throws java.lang.Exception
   */
  public void stop() {
    if (stop || serverSocket == null)
      return;
    log.info(getServiceName() +"server stopped ! ");
    //let's realease socket connection .
    stop = true;
    try {
      if (serverSocket != null)
        if (!serverSocket.isClosed())
          serverSocket.close();
      serverSocket = null;
    }
    catch (Exception e) {
      e.printStackTrace();
    }
//stop all process thread .
    while (free.size() != 0) {
      try {
        Processor processor = (Processor) free.removeFirst();

        processor.stop();
        try {
          synchronized (processor) {
            processor.notifyAll();
          }
        }
        catch (Exception ex) {

        }
      }
      catch (Exception alle) {

      }
    }

  }

  //==================================================variables
  protected ServerSocket serverSocket = null;

  protected boolean start = false;
  protected boolean stop = false;

  public static Log log = LogFactory.getLog(Server.class);
  public static void main(String[] args) throws Exception {

  }

}